package com.luych.toolbox.common.service.base;

import com.luych.toolbox.common.feign.FeignParam;
import com.luych.toolbox.common.feign.FeignView;
import com.luych.toolbox.common.page.PageParam;
import com.luych.toolbox.common.page.PageResult;
import com.luych.toolbox.common.service.page.SpringPageParamAdapter;
import com.luych.toolbox.common.service.page.SpringPageResultAdapter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;

public class BaseService<View extends FeignView, Param extends FeignParam, Entity extends BaseEntity> {

    private final JpaRepository<Entity, String> jpaRepository;
    private final Function<Param, Entity> paramToEntity;
    private final Function<Entity, View> entityToModel;

    public BaseService(JpaRepository<Entity, String> jpaRepository, Function<Param, Entity> paramToEntity, Function<Entity, View> entityToModel) {
        this.jpaRepository = jpaRepository;
        this.paramToEntity = paramToEntity;
        this.entityToModel = entityToModel;
    }

    protected View fill(View view) {
        return view;
    }

    public Optional<View> get(String id) {
        return jpaRepository.findById(id).map(this.entityToModel).map(this::fill);
    }

    public PageResult<View> page(PageParam pageParam) {
        Pageable pageable = new SpringPageParamAdapter(pageParam);
        Page<Entity> page = jpaRepository.findAll(pageable);
        PageResult<Entity> pageResult = new SpringPageResultAdapter<>(page);
        return pageResult.convert(this.entityToModel).convert(this::fill);
    }

    public View save(Param param) {
        Entity entity = this.paramToEntity.apply(param);
        Entity result = jpaRepository.save(entity);
        View view = this.entityToModel.apply(result);
        return this.fill(view);
    }

    public View create(Param param) {
        if (StringUtils.isEmpty(param.getId())) {
            param.setId(UUID.randomUUID().toString());
        }
        return this.save(param);
    }

    public View modify(Param param) {
        return this.save(param);
    }

    public void remove(String id) {
        jpaRepository.deleteById(id);
    }

    public List<View> batch(List<Param> params, List<Entity> entities) {
        for (Param param : params) {
            if (StringUtils.isEmpty(param.getId())) {
                param.setId(UUID.randomUUID().toString());
            }
        }
        List<View> views = jpaRepository.saveAll(params.stream().map(paramToEntity).collect(Collectors.toList()))
                .stream().map(entityToModel).collect(Collectors.toList());
        for (Entity entity : entities) {
            if (views.stream().map(View::getId).noneMatch(entity.getId()::equals)) {
                jpaRepository.delete(entity);
            }
        }
        return views;
    }

}
